#pragma once

#include <vector>

#include "exports.h"
#include "tester_interface.h"
#include "../util/coding.h"
#include "../db/write_batch.h"

using namespace std;
using namespace DB_NAME;

namespace dmock_test {

class WriteBatchTester : public Tester {
 public:
  void Init() override {
    for (int i = 0; i < 10; ++i) {
      kv_.emplace_back(to_string(1000 + i), to_string(1000 + i));
    }
    if (IS_NULL(batch_ = new WriteBatch)) {
      GLOB_LOG_ERROR("No Memory.");
    }
    assert(batch_);
  }
  void Run() override {
    GLOB_LOG_INFO("[Test.1] Write 1 KV");
    batch_->Put(SView(kv_[0].first), SView(kv_[0].second));
    assert(batch_->GetCount() == 1);
    check_batch(false);
    batch_->Clear();
    GLOB_LOG_INFO("ok.");

    GLOB_LOG_INFO("[Test.2] Write 10 KV");
    for (auto &kv : kv_) {
      batch_->Put(SView(kv.first), SView(kv.second));
    }
    assert(batch_->GetCount() == 10);
    check_batch(false);
    batch_->Clear();
    GLOB_LOG_INFO("ok.");

    GLOB_LOG_INFO("[Test.3] Write or deletion 10 KV");
    for (size_t i = 0; i < kv_.size(); ++i) {
      auto &kv = kv_[i];
      if (i % 2) {
        batch_->Put(SView(kv.first), SView(kv.second));
      } else {
        batch_->Delete(SView(kv.first));
      }
    }
    assert(batch_->GetCount() == 10);
    check_batch(true);
    batch_->Clear();
    GLOB_LOG_INFO("ok.");
  }
  void Close() override {
    if (batch_) {
      delete batch_;
    }
  }
 private:
  void check_batch(bool muti) {
    SView sv = batch_->Contents();
    BatchHeader *header = (BatchHeader *)sv.data();
    const char *data = header->data;
    for (uint32_t i = 0; i < header->count; ++i) {
      string type;
      string value;
      bool have_value = *(data++);
      string key = get_str(data);
      if (have_value) {
        value = get_str(data);
      }
      assert(kv_[i].first == key);
      if (have_value) {
        if (muti) {
          assert(i % 2);
        }
        assert(kv_[i].second == value);
      }
    }
  }
  string get_str(const char *&data) {
    uint64_t size = DecodeVarInt<uint64_t>(data);
    string str(data, size);
    data += size;
    return str;
  }
 private:
  vector<pair<string, string>> kv_;
  WriteBatch *batch_;
};

}